/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package com.lineage;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.lineage.configure.Config;
import com.lineage.server.utils.LeakCheckedConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * 提供用于访问DB数据库的各种接口.
 */
public final class L1DatabaseFactory {

    /** 提示信息. */
    private static final Logger LOG = Logger.getLogger(L1DatabaseFactory.class
            .getName());

    /** 数据库的实例. */
    private static L1DatabaseFactory instance;

    /** 数据库连接的来源. */
    private ComboPooledDataSource sourceSQL;

    // 连接数据库相关的资讯
    /** 数据库连接的驱动程序. */
    private static String driverSQL;
    /** 数据库连接的地址. */
    private static String urlSQL;
    /** 登入数据库的使用者名称. */
    private static String userSQL;
    /** 登入数据库的使用者密码. */
    private static String passwordSQL;

    /**
     * 取得数据库的实例（第一次实例为 null 的时候才新建立一个).
     * 
     * @return 数据库的实例
     * @throws SQLException
     *             该异常提供关于数据库访问错误或其他错误的信息。
     */
    public static L1DatabaseFactory getInstance() throws SQLException {
        if (instance == null) {
            instance = new L1DatabaseFactory();
        }
        return instance;
    }

    /**
     * 设置数据库登入的相关资讯.
     * 
     * @param driver
     *            数据库连接的驱动程序
     * @param url
     *            数据库连接的地址
     * @param user
     *            登入数据库的使用者名称
     * @param password
     *            登入数据库的使用者密码
     */
    public static void setDatabaseSettings(final String driver,
            final String url, final String user, final String password) {
        driverSQL = driver;
        urlSQL = url;
        userSQL = user;
        passwordSQL = password;
    }

    /**
     * 数据库连接的设置与配置.
     * 
     * @throws SQLException
     *             该异常提供关于数据库访问错误或其他错误的信息。
     */
    public L1DatabaseFactory() throws SQLException {
        try {
            // DatabaseFactory参考L2J
            this.sourceSQL = new ComboPooledDataSource();
            this.sourceSQL.setDriverClass(driverSQL);
            this.sourceSQL.setJdbcUrl(urlSQL);
            this.sourceSQL.setUser(userSQL);
            this.sourceSQL.setPassword(passwordSQL);

            // 测试连接
            this.sourceSQL.getConnection().close();
        } catch (final SQLException x) {
            LOG.fine("数据库连接失败.");
            // 重新抛出异常
            throw x;
        } catch (final Exception e) {
            LOG.fine("数据库连接失败.");
            throw new SQLException("无法初始化DB连接: " + e);
        }
    }

    /**
     * 取得数据库连接时的连线.
     * 
     * @return 连接对象or数据库资源泄漏检测
     */
    public Connection getConnection() {
        Connection con = null;

        while (con == null) {
            try {
                con = this.sourceSQL.getConnection();
            } catch (final SQLException e) {
                LOG.warning("取得数据库连接失败, 自动重试中... " + e);
            }
        }

        // 是否检测数据库内存泄露
        if (Config.DETECT_DB_RESOURCE_LEAKS) {
            return LeakCheckedConnection.create(con);
        } else {
            return con;
        }
    }

    /**
     * 服务器关闭的时候要关闭与数据库的连接.
     */
    public void shutdown() {
        try {
            this.sourceSQL.close();
        } catch (final Exception e) {
            LOG.log(Level.INFO, "", e);
        }
        try {
            this.sourceSQL = null;
        } catch (final Exception e) {
            LOG.log(Level.INFO, "", e);
        }
    }
}
